package com.example.myggnerate.unit;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.example.myggnerate.config.GenerateConfig;
import jakarta.annotation.Resource;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
public class MyGenerate {

    @Resource
    private GenerateConfig generateConfig;

    @Resource
    private MyTemplateEngine myTemplateEngine;

    private String outputDir;


    public void run() {
        outputDir = generateConfig.getOutputDir();
        //初始化输出目录
        if (generateConfig.isProject()) {
            outputDir = outputDir + File.separator + generateConfig.getProjectName();
        } else {
            if (outputDir == null || "".equals(outputDir)) {
                outputDir = System.getProperty("user.dir" );
            }
        }
//        File file = new File(OUTPUT_DIR);
//        if (!file.exists()) {
//            file.mkdirs();//初始化根目录
//        }
        //数据源设置
        FastAutoGenerator.create(generateConfig.getUrl(), generateConfig.getUsername(), generateConfig.getPassword())
                //全局配置
                .globalConfig(builder -> builder
                        .disableOpenDir()//禁止打开输出目录 默认值:true
                        .outputDir(outputDir + "/src/main/java" )//指定输出目录  默认值: windows:D:// linux or mac : /tmp
                        .author(generateConfig.getAuthor())//作者名 默认值:作者
//                        .enableKotlin()//开启 kotlin 模式 默认值:false
                        .enableSwagger()//开启 swagger 模式 	默认值:false
                        .dateType(DateType.TIME_PACK)//时间策略  DateType.ONLY_DATE 默认值: DateType.TIME_PACK
                        .commentDate(generateConfig.getCommentDate())//注释日期 默认值: yyyy-MM-dd
                        .build())
                // 包配置
                .packageConfig(builder -> builder
                        .parent(generateConfig.getParentPackage())//父包名 默认值:com.baomidou
                        .moduleName(generateConfig.getModuleNamePackage())//父包模块名 默认值:无
                        .entity(generateConfig.getEntityPackage())//Entity 包名 默认值:entity
                        .service(generateConfig.getServicePackage())//Service 包名 默认值:service
                        .serviceImpl(generateConfig.getServiceImplPackage())//Service Impl 包名 默认值:service.impl
                        .mapper(generateConfig.getMapperPackage())//Mapper 包名 默认值:mapper
                        .xml(generateConfig.getMapperXmlPackage())//Mapper XML 包名 默认值:mapper.xml
                        .controller(generateConfig.getControllerPackage())//Controller 包名 默认值:controller
                        .pathInfo(Collections.singletonMap(OutputFile.xml, outputDir + "/src/main/resources/mappers" ))//路径配置信息
                        .build())
                // 策略配置
//                .strategyConfig(builder -> builder
//                        //设置实体的模板
////                        .entityBuilder().javaTemplate("/templates/entity.java").disable() // 禁用实体生成
////                        .controllerBuilder().template("/templates/controller.java").disable()
////                        .mapperBuilder().disableMapper().disableMapperXml().mapperTemplate("/templates/mapper.java").mapperXmlTemplate("/templates/mapperXml.java")
//                        //禁用service接口生成
////                        .serviceBuilder().disableService().serviceTemplate("/templates/service.java").serviceImplTemplate("/templates/serviceImpl.java")
//                        .build())
                .strategyConfig(builder -> builder
                        .enableCapitalMode()//开启大写命名 默认值:false
                        .enableSkipView()//开启跳过视图 默认值:false
                        .disableSqlFilter()//禁用 sql 过滤 默认值:true，语法不能支持使用 sql 过滤表的话，可以考虑关闭此开关
//                        .enableSchema()//启用 schema 默认值:false，多 schema 场景的时候打开
//                        .likeTable(new LikeTable("USER"))//模糊表匹配(sql 过滤) likeTable 与 notLikeTable 只能配置一项
//                        .notLikeTable(new LikeTable("USER"))//模糊表排除(sql 过滤) likeTable 与 notLikeTable 只能配置一项
//                        .addInclude("t_simple")//增加表匹配(内存过滤) include 与 exclude 只能配置一项 ，支持正则匹配、例如 ^t_.* 所有 t_ 开头的表名
//                        .addTablePrefix("t_", "c_")//增加表排除匹配(内存过滤) include 与 exclude 只能配置一项 ，支持正则匹配、例如 .*st$ 所有 st 结尾的表名
                        //.addFieldPrefix("_flag")//增加过滤字段前缀
                        //.addFieldSuffix("_flag")//增加过滤字段后缀
                        .addTablePrefix(generateConfig.getTablePrefix())//增加过滤表前缀
                        .addTableSuffix(generateConfig.getTableSuffix())//增加过滤表后缀
                        .build())
                //Entity 策略配置
                .strategyConfig(builder -> builder
                        .entityBuilder() // 开始配置实体类的生成策略
                        .superClass(generateConfig.getEntitySuperClassName()) // 设置所有生成的实体类的父类为 BaseEntity
                        .disableSerialVersionUID() // 禁用生成 serialVersionUID
//                        .enableChainModel() // 开启链式模型
//                        .enableFileOverride() //开启文件覆盖 默认false
                        .enableLombok() // 开启 Lombok 支持
                        .enableRemoveIsPrefix() // 移除实体类字段名中的 "is" 前缀
                        .enableTableFieldAnnotation() // 开启实体类字段的表字段注解
                        .enableActiveRecord() // 开启 Active Record 模式
                        .versionColumnName(generateConfig.getVersionColumnName()) // 设置乐观锁字段名为 "version"
                        .versionPropertyName(generateConfig.getVersionPropertyName()) // 可选：设置乐观锁属性名，默认为 "version"
                        .logicDeleteColumnName(generateConfig.getLogicDeleteColumnName()) // 设置逻辑删除字段名为 "deleted"
                        .logicDeletePropertyName(generateConfig.getLogicDeletePropertyName()) // 可选：设置逻辑删除属性名，默认为 "deleteFlag"
                        .naming(NamingStrategy.underline_to_camel) // 设置数据库字段名命名策略为下划线转驼峰
                        .columnNaming(NamingStrategy.underline_to_camel) // 设置数据库字段名命名策略为下划线转驼峰
                        .addSuperEntityColumns(generateConfig.getSuperEntityColumns()) // 添加父类中的公共字段
//                        .addIgnoreColumns("age") // 添加需要忽略的字段名列表
                        .addTableFills(generateConfig.getTableFills()) // 表填充字段，设置创建时间字段在插入时自动填充
                        .idType(generateConfig.getIdType()) // 设置主键生成策略为雪花算法
                        .formatFileName(generateConfig.getEntityFormat()) // 设置生成文件名的格式，"%s" 会被替换为实体类名
                        .build())
                //Controller 策略配置
                .strategyConfig(builder -> builder
                        .controllerBuilder() // 开始配置控制器的生成策略
                        //.superClass(BaseController.class) // 设置所有生成的控制器的父类为 BaseController
                        .enableHyphenStyle() // 开启连字符风格的控制器命名，如 UserController -> user-controller
                        .enableRestStyle() // 开启 REST 风格的控制器命名，如 UserController -> UserControllerRest
                        .formatFileName(generateConfig.getControllerFormat()) // 设置生成文件名的格式，"%s" 会被替换为控制器类名
                        .build())
                //Service 策略配置
                .strategyConfig(builder -> {
                    if (generateConfig.isCanSuper()) {
                        builder
                                .serviceBuilder() // 开始配置服务和服务实现类的生成策略
                                .superServiceClass(IService.class) // 设置所有生成的服务接口的父接口为 IService
                                .superServiceImplClass(ServiceImpl.class) // 设置所有生成的服务实现类的父类为 ServiceImpl
                                .formatServiceFileName(generateConfig.getServiceFormat()) // 设置生成服务接口文件名的格式，"%s" 会被替换为服务接口类名
                                .formatServiceImplFileName(generateConfig.getServiceImplFormat()) // 设置生成服务实现类文件名的格式，"%s" 会被替换为服务实现类名
                                .build();
                    } else {
                        builder
                                .serviceBuilder() // 开始配置服务和服务实现类的生成策略
                                .formatServiceFileName(generateConfig.getServiceFormat()) // 设置生成服务接口文件名的格式，"%s" 会被替换为服务接口类名
                                .formatServiceImplFileName(generateConfig.getServiceImplFormat()) // 设置生成服务实现类文件名的格式，"%s" 会被替换为服务实现类名
                                .build();
                    }
                })
                //Mapper 策略配置
                .strategyConfig(builder -> builder
                        .mapperBuilder() // 开始配置 Mapper 接口和 XML 文件的生成策略
                        .superClass(BaseMapper.class) // 设置所有生成的 Mapper 接口的父接口为 BaseMapper
                        .mapperAnnotation(Mapper.class) // 开启 Mapper 接口的注解
                        .enableBaseResultMap() // 开启基本的结果映射
                        .enableBaseColumnList() // 开启基本的列集合
//                        .cache(MyMapperCache.class) // 设置 Mapper 缓存的实现类为 MyMapperCache
                        .formatMapperFileName(generateConfig.getMapperFormat()) // 设置生成 Mapper 接口文件名的格式，"%s" 会被替换为 Mapper 接口类名
                        .formatXmlFileName(generateConfig.getMapperXmlFormat()) // 设置生成 XML 文件名的格式，"%s" 会被替换为 XML 文件名
                        .build())
                //自定义      //在.ftl(或者是.vm)模板中，通过${cfg.author}获取属性
                .injectionConfig(consumer -> {
                    List<CustomFile> myCustomFiles = new ArrayList<>();

                    //实体类的公共父类
                    myCustomFiles.add(new MyCustomFile.Builder()
                            .totalRunTimes(1)
                            .fileName(generateConfig.getEntitySuperClassName() + ".java" )
                            .packageName(generateConfig.getEntityPackage() + ".base" )
                            .templatePath("/templates/baseEntity.java.vm" )
                            .enableFileOverride()
                            .build());
                    //Dto的公共父类
                    myCustomFiles.add(new MyCustomFile.Builder()
                            .totalRunTimes(1)
                            .fileName(generateConfig.getDtoSuperClassName() + ".java" )
                            .packageName(generateConfig.getDtoPackage() + ".base" )
                            .templatePath("/templates/baseDto.java.vm" )
                            .enableFileOverride()
                            .build());
                    //Vo的公共父类
                    myCustomFiles.add(new MyCustomFile.Builder()
                            .totalRunTimes(1)
                            .fileName(generateConfig.getVoSuperClassName() + ".java" )
                            .packageName(generateConfig.getVoPackage() + ".base" )
                            .templatePath("/templates/baseVo.java.vm" )
                            .enableFileOverride()
                            .build());
                    //Qo的公共父类
                    myCustomFiles.add(new MyCustomFile.Builder()
                            .totalRunTimes(1)
                            .fileName(generateConfig.getQoSuperClassName() + ".java" )
                            .packageName(generateConfig.getQoPackage() + ".base" )
                            .templatePath("/templates/baseQo.java.vm" )
                            .enableFileOverride()
                            .build());

                    myCustomFiles.add(new MyCustomFile.Builder()
                            .fileName(generateConfig.getDtoFormat())
                            .packageName(generateConfig.getDtoPackage())
                            .templatePath("/templates/dto.java.vm" )
                            .enableFileOverride()
                            .build());
                    myCustomFiles.add(new MyCustomFile.Builder()
                            .fileName(generateConfig.getQoFormat())
                            .packageName(generateConfig.getQoPackage())
                            .templatePath("/templates/qo.java.vm" )
                            .enableFileOverride()
                            .build());
                    myCustomFiles.add(new MyCustomFile.Builder()
                            .fileName(generateConfig.getVoFormat())
                            .packageName(generateConfig.getVoPackage())
                            .templatePath("/templates/vo.java.vm" )
                            .enableFileOverride()
                            .build());
                    if (generateConfig.isProject()) {
                        //启动类
                        String applicationName = generateConfig.getProjectName().substring(0, 1).toUpperCase() + generateConfig.getProjectName().substring(1) + "Application.java";
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .fileName(applicationName)
                                .enableFileOverride()
                                .templatePath("/templates/project/application.java.vm" )
                                .packageName("" ).build());

                        String resourcesPath = outputDir + "/src/main/resources/";
                        //添加 application.yml
                        String ymlTemplatePath = "/templates/project/yml/";
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .fileName("application.yml" )
                                .enableFileOverride()
                                .templatePath(ymlTemplatePath + "application.yml.vm" )
                                .filePath(resourcesPath).build());

                        //添加 application-dev.yml
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .fileName("application-dev.yml" )
                                .enableFileOverride()
                                .templatePath(ymlTemplatePath + "application-dev.yml.vm" )
                                .filePath(resourcesPath).build());

                        //添加 application-prod.yml
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .fileName("application-prod.yml" )
                                .enableFileOverride()
                                .templatePath(ymlTemplatePath + "application-prod.yml.vm" )
                                .filePath(resourcesPath).build());

                        //添加 pom.xml
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .fileName("pom.xml" )
                                .enableFileOverride()
                                .templatePath("/templates/project/pom.xml.vm" )
                                .filePath(outputDir).build());

                        //添加 R
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .packageName(generateConfig.getrPackage())
                                .fileName("R.java" )
                                .enableFileOverride()
                                .templatePath("/templates/project/response/R.java.vm" )
                                .build());

                        //添加常量
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .packageName("constants" )
                                .fileName("Constants.java" )
                                .enableFileOverride()
                                .templatePath("/templates/project/constants/constants.java.vm" )
                                .build());
                        //添加常量
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .packageName("constants" )
                                .fileName("ResponseStrConstants.java" )
                                .enableFileOverride()
                                .templatePath("/templates/project/constants/responseStrConstants.java.vm" )
                                .build());
                        //添加常量
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .packageName("constants" )
                                .fileName("StateConstants.java" )
                                .enableFileOverride()
                                .templatePath("/templates/project/constants/stateConstants.java.vm" )
                                .build());

                        //添加权限表
                        if (generateConfig.isHasPower()) {
                            //用户相关threadLocal
                            myCustomFiles.add(new MyCustomFile.Builder()
                                    .totalRunTimes(1)
                                    .packageName("threadLocal" )
                                    .fileName("UserThreadLocal.java" )
                                    .enableFileOverride()
                                    .templatePath("/templates/project/threadLocal/userThreadLocal.java.vm" )
                                    .build());
                            //权限表
                            myCustomFiles.add(new MyCustomFile.Builder()
                                    .totalRunTimes(1)
                                    .packageName("service" )
                                    .fileName("PermissionService.java" )
                                    .enableFileOverride()
                                    .templatePath("/templates/project/service/permissionService.java.vm" )
                                    .build());
                        }
                        //添加 configs
//                        myCustomFiles.add(new MyCustomFile.Builder()
//                                .totalRunTimes(1)
//                                .packageName("config")
//                                .fileName("pom.xml")
//                                .enableFileOverride()
//                                .templatePath("/templates/application.yml.vm")
//                                .build());

                        String utilsTemplatePath = "/templates/project/utils/";
                        //添加 utils
                        myCustomFiles.add(new MyCustomFile.Builder()
                                .totalRunTimes(1)
                                .packageName("utils" )
                                .fileName("MyUtils.java" )
                                .enableFileOverride()
                                .templatePath(utilsTemplatePath + "MyUtils.java.vm" )
                                .build());
                    }
                    consumer.customFile(myCustomFiles);

                })
                //自定义配置 Map 对象(参数)
                .injectionConfig(consumer ->
                {
                    String ip = getIpFromUrl(generateConfig.getUrl());
                    Map<String, Object> customMap = new HashMap<>();
                    customMap.put("projectName", generateConfig.getProjectName());
                    customMap.put("groupId", generateConfig.getParentPackage());
                    customMap.put("dataIp", ip);
                    customMap.put("dataUrl", generateConfig.getUrl());
                    customMap.put("dataUserName", generateConfig.getUsername());
                    customMap.put("dataPassWord", generateConfig.getPassword());
                    customMap.put("doMainName", generateConfig.getDoMainName());
                    customMap.put("superDtoClass", generateConfig.getDtoSuperClassName());
                    customMap.put("superVoClass", generateConfig.getVoSuperClassName());
                    customMap.put("superQoClass", generateConfig.getQoSuperClassName());
                    customMap.put("errorThrow", generateConfig.isErrorThrow());//参数校验不通过，以报错形式抛出
                    customMap.put("hasPower", generateConfig.isHasPower());//是否携带权限认证机制
                    customMap.put("RName", "R" );
                    consumer.customMap(customMap);
//                    consumer.build().


                })
                .templateEngine(myTemplateEngine)//设置引擎模板
                .execute();

    }

    /**
     * 提取IP
     *
     * @param url
     * @return
     */
    private String getIpFromUrl(String url) {
        Pattern pattern = Pattern.compile("\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b" );
        Matcher matcher = pattern.matcher(url);
        StringBuilder ipBuilder = new StringBuilder();
        while (matcher.find()) {
            ipBuilder.append(matcher.group()).append(" " );
        }
        return ipBuilder.toString().split("\\s+" )[0];
    }


    public final static class MyCustomFile extends CustomFile {
        private Function<TableInfo, String> formatNameFunction;
        private String fileName;
        private String templatePath;
        private String packageName;
        private String filePath;
        private boolean fileOverride;
        private int totalRunTimes = -1; //允许的总生成次数  -1代表和实体类保持一致
        private int currentRunTimes = 0;//当前生成次数

        public int getCurrentRunTimes() {
            return currentRunTimes;
        }

        public int getTotalRunTimes() {
            return totalRunTimes;
        }

        @Override
        public boolean isFileOverride() {
            return fileOverride;
        }

        @Override
        public String getFilePath() {
            return filePath;
        }

        @Override
        public String getPackageName() {
            return packageName;
        }

        @Override
        public String getTemplatePath() {
            return templatePath;
        }

        @Override
        public String getFileName() {
            return fileName;
        }

        @Override
        public Function<TableInfo, String> getFormatNameFunction() {
            return formatNameFunction;
        }

        public void setCurrentRunTimes(int currentRunTimes) {
            this.currentRunTimes = currentRunTimes;
        }

        public MyCustomFile() {
        }

        public static class Builder {
            private final MyCustomFile customFile = new MyCustomFile();

            public Builder() {
            }

            public MyCustomFile.Builder totalRunTimes(int totalRunTimes) {
                this.customFile.totalRunTimes = totalRunTimes;
                return this;
            }

            public MyCustomFile.Builder currentRunTimes(int currentRunTimes) {
                this.customFile.currentRunTimes = currentRunTimes;
                return this;
            }


            public MyCustomFile.Builder formatNameFunction(Function<TableInfo, String> formatNameFunction) {
                this.customFile.formatNameFunction = formatNameFunction;
                return this;
            }

            public MyCustomFile.Builder fileName(String fileName) {
                this.customFile.fileName = fileName;
                return this;
            }

            public MyCustomFile.Builder templatePath(String templatePath) {
                this.customFile.templatePath = templatePath;
                return this;
            }

            public MyCustomFile.Builder packageName(String packageName) {
                this.customFile.packageName = packageName;
                return this;
            }

            public MyCustomFile.Builder filePath(String filePath) {
                this.customFile.filePath = filePath;
                return this;
            }

            public MyCustomFile.Builder enableFileOverride() {
                this.customFile.fileOverride = true;
                return this;
            }

            public MyCustomFile build() {
                return this.customFile;
            }

        }
    }


}